﻿Imports Microsoft.VisualStudio.DebuggerVisualizers
Imports System.ComponentModel
Imports System.Data.Common
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Imports System.IO
Imports System.Reflection
Imports System.Runtime.Serialization
Imports System.Text

Public Class ObjectQueryVisualizerObjectSource
    Inherits VisualizerObjectSource

    '''' <summary>
    '''' </summary>
    '''' <param name="target"></param>
    '''' <param name="outgoingData"></param>
    '''' <remarks>Warning: detached entities will not show up!</remarks>
    Public Overrides Sub GetData(ByVal target As Object, ByVal outgoingData As Stream)
        Dim objectQuery As ObjectQuery = DirectCast(target, ObjectQuery)

        Dim ds As New DataSet()
        ' table with query values
        ds.Tables.Add(Constants.TABLE_VALUES)
        ds.Tables(Constants.TABLE_VALUES).Columns.Add(Constants.COLUMN_STATE).DataType = GetType(String)
        ' table with added values
        ds.Tables.Add(Constants.TABLE_ADDED)
        ds.Tables(1).Columns.Add(Constants.COLUMN_STATE).DataType = GetType(String)

        ' find the type of the entities in the objectquery
        Dim entitySampleType As Type = Nothing
        For Each entitySample In objectQuery
            entitySampleType = entitySample.GetType()
            Exit For
        Next

        If (entitySampleType IsNot Nothing) Then
            For Each obj In objectQuery
                Dim entity = TryCast(obj, EntityObject)
                If (entity IsNot Nothing) Then
                    Dim ose As ObjectStateEntry = Nothing
                    If entity Is Nothing OrElse entity.EntityKey Is Nothing OrElse _
                            Not objectQuery.Context.ObjectStateManager.TryGetObjectStateEntry(entity.EntityKey, ose) Then
                        Continue For
                    Else
                        AddEntityToTable(ds.Tables(Constants.TABLE_VALUES), ose)
                    End If
                Else
                    AddSelfTrackingEntityToTable(ds.Tables(Constants.TABLE_VALUES), obj)
                End If
            Next

            ' this query will produce the added entities, regardless if they would belong to the query
            Dim oseQuery = From ose In objectQuery.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added) _
                            Where ose.Entity IsNot Nothing _
                            AndAlso ose.Entity.GetType Is entitySampleType

            For Each ose As ObjectStateEntry In oseQuery
                AddEntityToTable(ds.Tables(Constants.TABLE_ADDED), ose)
            Next

            ds.AcceptChanges()
        End If

        ds.ExtendedProperties.Add(Constants.EXT_PROP_TRACESTRING, objectQuery.ToTraceString().Replace(Environment.NewLine, " "))

        ds.WriteXml(outgoingData, XmlWriteMode.WriteSchema)
    End Sub

    Private Sub AddEntityToTable(ByVal dt As DataTable, ByVal ose As ObjectStateEntry)
        Dim values As IExtendedDataRecord
        If (ose.State = EntityState.Deleted) Then
            values = DirectCast(ose.OriginalValues, IExtendedDataRecord)
        Else
            values = ose.CurrentValues
        End If
        If (dt.Columns.Count = 1) Then
            ' create the datatable columns from the first entry
            For i = 0 To values.FieldCount - 1
                'metadata provides field names
                Dim name = values.DataRecordInfo.FieldMetadata(i).FieldType.Name
                dt.Columns.Add(name) '.DataType = GetType(String)
                dt.Columns.Add(name & " changed").DataType = GetType(Boolean)
            Next
        End If

        Dim row As DataRow = dt.NewRow()

        row(Constants.COLUMN_STATE) = ose.State.ToString()

        For i = 0 To values.FieldCount - 1
            row(i * 2 + 1) = values(i)
            If (ose.State = EntityState.Deleted OrElse ose.State = EntityState.Added) Then
                row(i * 2 + 2) = True
            Else
                Dim name = values.DataRecordInfo.FieldMetadata(i).FieldType.Name
                Dim changedProp As String = (From prop In ose.GetModifiedProperties _
                                             Where prop = name).FirstOrDefault
                Dim isModified As Boolean = (changedProp IsNot Nothing)
                row(i * 2 + 2) = isModified
            End If
        Next
        dt.Rows.Add(row)
    End Sub

    Private Sub AddSelfTrackingEntityToTable(ByVal dt As DataTable, entity As Object)

        Static dataMemberProps As List(Of PropertyInfo) = Nothing

        If (dt.Columns.Count = 1) Then
            ' create the datatable columns from the first entry
            dataMemberProps = Utility.GetPropertyInfos(entity)
            For Each prop As PropertyInfo In dataMemberProps
                dt.Columns.Add(prop.Name)
                dt.Columns.Add(prop.Name & " changed").DataType = GetType(Boolean)
            Next
        End If

        Dim row As DataRow = dt.NewRow()

        Dim state As String = Utility.GetStateFromEntity(entity)
        row(Constants.COLUMN_STATE) = state

        For i = 0 To dataMemberProps.Count - 1
            Dim value As Object = dataMemberProps(i).GetValue(entity, Nothing)
            row(i * 2 + 1) = Utility.NullableObjectToString(value)
            row(i * 2 + 2) = (state = "Deleted") OrElse (state = "Added") OrElse (state = "Modified" AndAlso Utility.IsPropertyChanged(entity, dataMemberProps(i).Name))
        Next
        dt.Rows.Add(row)
    End Sub

End Class
